/*
 * Copyright 2011 Dominik Pretzsch <dominik.pretzsch at gmail.com>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.blacksheep.jmoteextract.remote;

import de.blacksheep.jmoteextract.remote.socket.ClientSocketFactory;
import de.blacksheep.jmoteextract.remote.socket.ServerSocketFactory;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.rmi.server.UnicastRemoteObject;
import de.blacksheep.jmoteextract.archive.ArchiveStorage;
import de.blacksheep.jmoteextract.configuration.Configuration;
import de.blacksheep.jmoteextract.mvc.model.MainModel;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * <code>RemoteServer</code> provides a JMoteExtractRemoteServer instance <br>
 * at <code>//127.0.0.1/JMoteExtractRemoteServer</code>. That server can be <br>
 * provided with <code>RemoteArchive</code>s to queue on the local JMoteExtract <br>
 * instance's <code>ExtractionQueue</code>. <br>
 * <br>
 * The server is accessed using RMI calls.
 * 
 * @author Dominik Pretzsch <dominik.pretzsch at gmail.com>
 * @see RemoteArchive
 * @see ExtractionQueue
 * @see MainModel#extract(java.lang.String)  
 */
public class RemoteServer implements IRemoteServer {
    
    private static int    serverPort = 0;
    private static String serverName = null;
    private static RemoteServer instance;
    
    private static final byte pattern = 0x3D;    
    private static RMIClientSocketFactory clientFactory = new ClientSocketFactory(pattern);
    private static RMIServerSocketFactory serverFactory = new ServerSocketFactory(pattern);
    
    private static final Logger LOGGER =  Logger.getLogger(RemoteServer.class.getName());

    private RemoteServer() throws RemoteException {
        super();
        init();
    }
    
    public static void init() {
        Configuration config = Configuration.getInstance();
        if (serverName == null) {
            serverName = config.getProperty(Configuration.PROP.SERVER, "serverName");
        }
        if (serverPort == 0) {
            serverPort = Integer.valueOf(config.getProperty(Configuration.PROP.SERVER, "serverPort"));
        }
    }
    
    public static RemoteServer getInstance() throws RemoteException {
        if (instance == null) {
            instance = new RemoteServer();
        }
        return instance;
    }
    
    /**
     * Static method to register a new <code>RemoteServer</code> to <br>
     * the local RMI registry. <br>
     * <br>
     * The <code>RemoteServer</code> can be accessed using RMI calls <br>
     * to <code>//127.0.0.1/JMoteExtractRemoteServer</code>.
     */
    public static void registerServer() throws Exception {
        Registry registry = null;
        init();
        
        try {
            /*registry = LocateRegistry.createRegistry(serverPort, 
                                                     clientFactory, 
                                                     serverFactory);*/
            LocateRegistry.createRegistry(serverPort);
            registry = LocateRegistry.getRegistry(serverPort);
        } catch (RemoteException ex) {
            // TODO: RemoteException
            LOGGER.log(Level.SEVERE, "RemoteException (REGISTRY)", ex);
            throw new Exception();
        }        
        
        try {
            RemoteServer server = RemoteServer.getInstance();
            IRemoteServer stub = 
                    (IRemoteServer) 
                    UnicastRemoteObject.exportObject(server, 
                                                     0, 
                                                     clientFactory, 
                                                     serverFactory);
                    //UnicastRemoteObject.exportObject(server, 0); 
            registry.rebind(serverName, stub);
        } catch (RemoteException ex) {
            // TODO: RemoteException
            LOGGER.log(Level.SEVERE, "RemoteException (REBIND)", ex);
            throw new Exception();
        } catch (NullPointerException ex) {
            // TODO: NullPointerException
            LOGGER.log(Level.SEVERE, "NullPointerException", ex);
            throw new Exception();
        }    
    }
    
    /**
     * Looks up and returns an instance of <code>RemoteServer</code>. <br>
     * This instance is an RMI object.
     * 
     * @param host   The URL to the <code>RemoteServer</code> <br>
     *               (e.g. <code>//127.0.0.1/JMoteExtractRemoteServer</code>)
     * @return RemoteServerInterface or <code>null</code> on error.
     */
    public static IRemoteServer getServer(String host) {
        init();        
        LOGGER.log(Level.FINE, "Getting RemoteServer");
        
        IRemoteServer server = null;        
        try {
            LOGGER.log(Level.FINE, "Getting remote RMI registry on {0}", host);
            Registry registry = 
                    /*LocateRegistry.getRegistry(host,
                                               serverPort, 
                                               clientFactory);*/
                    LocateRegistry.getRegistry(host, serverPort);
            
            LOGGER.log(Level.FINE, "Looking-up registry for RemoteServer");
            server = (IRemoteServer) registry.lookup(serverName);
        } catch (Exception ex) {
            // TODO: Exception
            LOGGER.log(Level.SEVERE, "Exception", ex);
        }        
        return server;
    }
    
    /**
     * Adds an archive to the server's local <code>ExtractionQueue</code>.
     * 
     * @param rArchive  The <code>RemoteArchive</code> describing the <code>Archive</code> object
     * @return <code>true</code> if the archive was enqueued; <code>false</code> otherwise
     * @see RemoteArchive
     * @see Archive
     * @see ExtractionQueue
     */
    public int addArchive(RemoteArchive rArchive) {
        LOGGER.log(Level.FINE, "Adding RemoteArchive to local storage");
        
        ArchiveStorage as = ArchiveStorage.getInstance();
        return as.createLocalArchive(rArchive);
    }
    
    public void assignObserver(int hash, IRemoteObserver remoteObserver) 
            throws RemoteException {
        
        LOGGER.log(Level.FINE, "Setting RemoteObserver object to local storage");
        
        ArchiveStorage as = ArchiveStorage.getInstance();
        as.assignObserver(hash, remoteObserver);
    }
}
